import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class JVMMemoryApplication {
    // 静态变量存储在方法区
    private static final String STATIC_CONSTANT = "静态常量";
    private static List<Object> staticList = new ArrayList<>();

    // 实例变量存储在堆中
    private String instanceVariable = "实例变量";
    private List<String> instanceList = new ArrayList<>();

    public static void main(String[] args) {
        // 1. 内存区域演示
        //memoryAreaDemo();

         // 2. 堆内存分析
         //heapMemoryDemo();

         // 3. 栈内存演示
         //stackMemoryDemo();

         // 4. 方法区演示
         methodAreaDemo();

        // 5. 内存溢出演示
        // memoryOverflowDemo(); // 注释掉避免实际溢出
    }

    /**
     * 内存区域演示
     */
    private static void memoryAreaDemo() {
        System.out.println("=== JVM内存区域演示 ===");

        // 获取内存管理信息
        MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
        MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();

        System.out.println("堆内存使用情况:");
        System.out.println("  初始大小: " + formatBytes(heapUsage.getInit()));
        System.out.println("  已使用: " + formatBytes(heapUsage.getUsed()));
        System.out.println("  已提交: " + formatBytes(heapUsage.getCommitted()));
        System.out.println("  最大大小: " + formatBytes(heapUsage.getMax()));

        System.out.println("非堆内存使用情况:");
        System.out.println("  初始大小: " + formatBytes(nonHeapUsage.getInit()));
        System.out.println("  已使用: " + formatBytes(nonHeapUsage.getUsed()));
        System.out.println("  已提交: " + formatBytes(nonHeapUsage.getCommitted()));
        System.out.println("  最大大小: " + formatBytes(nonHeapUsage.getMax()));

        // 获取各个内存池的信息
        List<MemoryPoolMXBean> memoryPools = ManagementFactory.getMemoryPoolMXBeans();
        System.out.println("\n内存池详细信息:");
        for (MemoryPoolMXBean pool : memoryPools) {
            MemoryUsage usage = pool.getUsage();
            System.out.println("  " + pool.getName() + ":");
            System.out.println("    类型: " + pool.getType());
            System.out.println("    已使用: " + formatBytes(usage.getUsed()));
            System.out.println("    最大大小: " + formatBytes(usage.getMax()));
        }

        System.out.println();
    }

    /**
     * 堆内存演示
     */
    private static void heapMemoryDemo() {
        System.out.println("=== 堆内存演示 ===");

        // 创建对象演示堆内存分配
        List<String> localList = new ArrayList<>();

        System.out.println("创建对象前堆内存使用:");
        printHeapMemory();

        // 分配大量对象
        for (int i = 0; i < 100000; i++) {
            localList.add("对象" + i);
        }

        System.out.println("创建10万个对象后堆内存使用:");
        printHeapMemory();

        // 触发垃圾回收
        System.gc();

        try {
            Thread.sleep(1000); // 等待GC完成
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("垃圾回收后堆内存使用:");
        printHeapMemory();

        // 清空引用，使对象可被回收
        localList.clear();
        localList = null;

        System.gc();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("清空引用并GC后堆内存使用:");
        printHeapMemory();

        System.out.println();
    }

    /**
     * 栈内存演示
     */
    private static void stackMemoryDemo() {
        System.out.println("=== 栈内存演示 ===");

        // 演示方法调用和栈帧
        System.out.println("开始递归调用演示栈帧:");
        try {
            recursiveMethod(1, 5);
        } catch (Exception e) {
            System.out.println("递归调用完成");
        }

        // 演示局部变量表
        demonstrateLocalVariables();

        System.out.println();
    }

    /**
     * 递归方法演示栈帧
     */
    private static void recursiveMethod(int current, int max) {
        // 局部变量存储在栈帧的局部变量表中
        String localVar = "第" + current + "层调用";
        int[] localArray = new int[10]; // 引用存储在栈中，数组对象在堆中

        System.out.println("  " + localVar + " - 栈深度: " + current);

        if (current < max) {
            recursiveMethod(current + 1, max);
        }

        System.out.println("  " + localVar + " - 返回");
    }

    /**
     * 演示局部变量表
     */
    private static void demonstrateLocalVariables() {
        System.out.println("局部变量表演示:");

        // 基本类型变量直接存储在局部变量表中
        int intVar = 42;
        long longVar = 123456789L;
        double doubleVar = 3.14159;
        boolean boolVar = true;

        // 引用类型变量的引用存储在局部变量表中，对象在堆中
        String stringVar = "Hello JVM";
        Object objectVar = new Object();
        int[] arrayVar = {1, 2, 3, 4, 5};

        System.out.println("  基本类型变量 (存储在栈中):");
        System.out.println("    int: " + intVar);
        System.out.println("    long: " + longVar);
        System.out.println("    double: " + doubleVar);
        System.out.println("    boolean: " + boolVar);

        System.out.println("  引用类型变量 (引用在栈中，对象在堆中):");
        System.out.println("    String: " + stringVar);
        System.out.println("    Object: " + objectVar);
        System.out.println("    Array: " + Arrays.toString(arrayVar));
    }

    /**
     * 方法区演示
     */
    private static void methodAreaDemo() {
        System.out.println("=== 方法区演示 ===");

        // 静态变量存储在方法区
        System.out.println("静态常量: " + STATIC_CONSTANT);

        // 类信息存储在方法区
        Class<?> clazz = JVMMemoryApplication.class;
        System.out.println("类信息:");
        System.out.println("  类名: " + clazz.getName());
        System.out.println("  父类: " + clazz.getSuperclass().getName());
        System.out.println("  方法数量: " + clazz.getDeclaredMethods().length);
        System.out.println("  字段数量: " + clazz.getDeclaredFields().length);

        // 字符串常量池演示
        String str1 = "Hello";  // 字面量，存储在字符串常量池
        String str2 = "Hello";  // 复用常量池中的对象
        String str3 = new String("Hello");  // 在堆中创建新对象

        System.out.println("字符串常量池演示:");
        System.out.println("  str1 == str2: " + (str1 == str2));  // true
        System.out.println("  str1 == str3: " + (str1 == str3));  // false
        System.out.println("  str1.equals(str3): " + str1.equals(str3));  // true

        // 运行时常量池
        System.out.println("运行时常量池中的常量数量: " +
                clazz.getDeclaredFields().length);

        System.out.println();
    }

    /**
     * 内存溢出演示（注释掉避免实际溢出）
     */
    @SuppressWarnings("unused")
    private static void memoryOverflowDemo() {
        System.out.println("=== 内存溢出演示 ===");

        try {
            // 1. 堆内存溢出 OutOfMemoryError: Java heap space
            List<byte[]> heapOOM = new ArrayList<>();
            while (true) {
                heapOOM.add(new byte[1024 * 1024]); // 每次分配1MB
            }
        } catch (OutOfMemoryError e) {
            System.out.println("堆内存溢出: " + e.getMessage());
        }

        try {
            // 2. 栈溢出 StackOverflowError
            infiniteRecursion();
        } catch (StackOverflowError e) {
            System.out.println("栈溢出: " + e.getMessage());
        }

        try {
            // 3. 方法区溢出 OutOfMemoryError: Metaspace (JDK8+)
            // 需要特殊配置才能触发，这里只是示例
            System.out.println("方法区溢出需要动态生成大量类");
        } catch (OutOfMemoryError e) {
            System.out.println("方法区溢出: " + e.getMessage());
        }
    }

    /**
     * 无限递归导致栈溢出
     */
    private static void infiniteRecursion() {
        infiniteRecursion(); // 无限递归
    }

    /**
     * 打印堆内存使用情况
     */
    private static void printHeapMemory() {
        MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();

        System.out.println("  已使用: " + formatBytes(heapUsage.getUsed()) +
                " / 最大: " + formatBytes(heapUsage.getMax()) +
                " (" + String.format("%.1f",
                (double) heapUsage.getUsed() / heapUsage.getMax() * 100) + "%)");
    }

    /**
     * 格式化字节数
     */
    private static String formatBytes(long bytes) {
        if (bytes < 0) return "N/A";

        String[] units = {"B", "KB", "MB", "GB", "TB"};
        int unitIndex = 0;
        double size = bytes;

        while (size >= 1024 && unitIndex < units.length - 1) {
            size /= 1024;
            unitIndex++;
        }

        return String.format("%.2f %s", size, units[unitIndex]);
    }
}
